import fs from "fs-extra";
import moment from "moment";
import path from "path";
import { BaseTask, TaskResult } from "../../common/tasks/BastTask.js";
import { toolchain } from "../../toolchain.js";
import { UploadResult, WechatTools } from "../../tools/WechatTools.js";
import { WebGLEnv } from "../WebGLEnv.js";
import { CheckEnvTask } from "../../common/tasks/CheckEnvTask.js";
import { Nullable } from "../../typings.js";
import { readVersionTxt, unzipTo, safeCopyFile, findFiles, md5 } from "../../tools/vendor.js";

export interface WebGLMiniGameUploadData {
    success: boolean
    result: UploadResult | null
}

export class WebGLMiniGameUploadTask extends BaseTask<WebGLMiniGameUploadData> {
    async run(): Promise<TaskResult<WebGLMiniGameUploadData>> {
        const exportRoot = path.join(toolchain.params.workSpacePath, WebGLEnv.ExportMiniGameRoot, WebGLEnv.MiniGameRoot);

        // // 预览
        // const qrcode = path.join(toolchain.params.workSpacePath, WebGLEnv.MiniGameQrcode);
        // const previewOutput = path.join(toolchain.params.workSpacePath, WebGLEnv.MiniGamePreviewOutput);
        // const previewRet = await WechatTools.preview(exportRoot, qrcode, previewOutput);
        // if (previewRet.data) {
        //     // 生成最新的预览页面
        //     const tpl = await fs.readFile(path.join(toolchain.__dirname, '../templates/minigame.html'), 'utf-8');
        //     const qrcodeBase64 = await fs.readFile(qrcode, 'utf-8');
        //     const html = tpl.replace('${title}', toolchain.params.channelCfg.productName).
        //     replace('${time}', moment().format('YYYY_MM_DD_HH_mm_ss')).
        //     replace('${qrcode}', qrcodeBase64);
        //     await fs.ensureDir(toolchain.params.packageLocalWebPath);
        //     await fs.writeFile(path.join(toolchain.params.packageLocalWebPath, 'index.html'), html, 'utf-8');
        // } else {
        //     console.error('预览失败！');
        // }
        
        // 上传
        // 读取版本号，注意不能直接读取project目录下的版本号，因可能本次上传任务是由于上次构建自动上传失败后手动发起的，而这过程中可能构建过其他版本
        // 应该从export目录底下的版本号记录文件读取版本号
        const appVerFile = path.join(toolchain.params.workSpacePath, WebGLEnv.MiniGameAppVerFile);
        const appVer = await fs.readFile(appVerFile, 'utf-8');
        const resVerFile = path.join(toolchain.params.workSpacePath, WebGLEnv.MiniGameResVerFile);
        const [resVer] = await readVersionTxt(resVerFile);
        // 记录本次版本号
        const fullVer = `${appVer}(${resVer})`;
        toolchain.unity.commitFullBuildVer(fullVer);
        
        const ver = appVer + '.b' + resVer;
        const desc = this.cmdOption.uploadMinigameDesc + '@' + moment().format(moment.HTML5_FMT.DATETIME_LOCAL);
        const smp = path.join(toolchain.params.workSpacePath, `../.build/${fullVer}.zip`);
        await fs.ensureDir(path.dirname(smp));
        const uploadOutput = path.join(toolchain.params.workSpacePath, WebGLEnv.MiniGameUploadOutput);

        const canCi = await WechatTools.canWorkWithCi(exportRoot);
        let tryTimes = 0, success = false, result: UploadResult | null = null;
        while(tryTimes < 3) {
            tryTimes++;
            const uploadRet = canCi ? await WechatTools.uploadByCi(exportRoot, smp, ver, desc) : await WechatTools.upload(exportRoot, ver, desc, uploadOutput);
    
            if (!uploadRet.data.success) {
                console.error('上传失败！');
                if (uploadRet.data.needLogin) {
                    // 需要重新登录，不重试
                    break;
                }
            } else {
                success = true;
                result = uploadRet.data;
                break;
            }
        }
        if (!success) {
            toolchain.buildMessages.push('小游戏`上传失败`，请重新上传');
        } else {
            if (fs.existsSync(smp)) {
                await this.copyTsscriptMaps(resVer, smp);
            } else {
                toolchain.buildMessages.push('下载sourcemap失败');
            }
        }

        return { success: true, errorCode: 0, data: { success, result } };
    }

    /**
     * copy ts的map到资源服务器上
     **/
    private async copyTsscriptMaps(resver: string, smpZipPath: string): Promise<void> {
        console.log('@@ start copy sourcemap ...');
        const dstmaps = path.join(toolchain.params.uploadPath, toolchain.unity.getAssetsDir(), 'tsscriptmaps');
        fs.ensureDir(dstmaps);

        const srcmaps = smpZipPath.substring(0, smpZipPath.length - 4); // 去掉.zip后缀
        await unzipTo(srcmaps, smpZipPath);
        await safeCopyFile(srcmaps + '/__APP__/game.js.map', srcmaps + '/game.js.map');
        await safeCopyFile(srcmaps + '/__APP__/workers.js.map', srcmaps + '/workers.js.map');
        
        const md5map: {[key: string]: string} = {};
        
        const files = await findFiles(srcmaps, ['.map'], '+');
        for(let file of files) {
            if (file.includes('__APP__') || file.includes('__FULL__')) continue;
            let fileRelativePath = path.relative(srcmaps, file).replace(/\\/g, '/');
            let fileNameFormated = fileRelativePath.substring(0, fileRelativePath.length - 7);
            md5map[fileNameFormated] = md5(fs.readFileSync(file, 'utf-8'));
            const dstfile = path.join(dstmaps, fileNameFormated + '.' + md5map[fileNameFormated] + '.js.map');
            fs.ensureDirSync(path.dirname(dstfile));
            fs.copyFileSync(file, dstfile);
        }
        const resverFile = path.join(dstmaps, 'ver.' + resver + '.txt');
        fs.ensureDirSync(path.dirname(resverFile));
        fs.writeFileSync(resverFile, JSON.stringify(md5map));
        console.log('@@ copy sourcemap ok');
    }

    get dependencies(): Nullable<string[]> {
        return [CheckEnvTask.name]
    }
}
